﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace DiscoveryLogic.UI.Charts
{
    public class AxisLabels:ObservableObject
    {
        #region fields
        private double _MaxRowHeight;
        private double _SavedFontSize;
        private int _SavedRows;
        private double _SavedAngle;
        private bool _IsRedraw;
        private Axis _Parent;
        #endregion

        #region ctor
        public AxisLabels()
        {
            DefaultStyleKey = typeof (AxisLabels);
        }
        #endregion

        #region props
        public static DependencyProperty IntervalProperty =
        DependencyProperty.Register(
          "Interval",
          typeof(Nullable<double>), typeof(AxisLabels),
          new PropertyMetadata(OnIntervalPropertyChanged));

        private static void OnIntervalPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            AxisLabels _AxisLabels = d as AxisLabels;
            _AxisLabels.FirePropertyChanged("Interval");
        }

        [TypeConverter(typeof(Converters.NullableDoubleConverter))]
        public Nullable<double> Interval
        {
            get
            {
                if ((Nullable<double>)GetValue(IntervalProperty) == null)
                    return double.NaN;
                else
                    return ((Nullable<double>) base.GetValue(IntervalProperty));
            }
            set { base.SetValue(IntervalProperty, value); }
        }

        public static DependencyProperty AngleProperty =
        DependencyProperty.Register(
          "Angle",
          typeof(Nullable<double>), typeof(AxisLabels),
          new PropertyMetadata(OnAnglePropertyChanged));

        private static void OnAnglePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            AxisLabels _AxisLabels = d as AxisLabels;
            _AxisLabels.FirePropertyChanged("Angle");
        }

        [TypeConverter(typeof(Converters.NullableDoubleConverter))]
        public Nullable<double> Angle
        {
            get
            {
                if ((Nullable<double>)GetValue(AngleProperty) == null)
                    return double.NaN;
                else
                    return ((Nullable<double>) base.GetValue(AngleProperty));
            }
            set { base.SetValue(AngleProperty, value); }
        }

        public static DependencyProperty EnabledProperty =
        DependencyProperty.Register(
          "Enabled",
          typeof(Nullable<bool>), typeof(AxisLabels),
          new PropertyMetadata(OnEnabledPropertyChanged));

        private static void OnEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            AxisLabels _AxisLabels = d as AxisLabels;
            _AxisLabels.FirePropertyChanged("Enabled");
        }

        [TypeConverter(typeof(NullableBoolConverter))]
        public Nullable<bool> Enabled
        {
            get
            {
                if ((Nullable<bool>)GetValue(EnabledProperty) == null)
                    return true;
                else
                    return ((Nullable<bool>) base.GetValue(EnabledProperty));
            }
            set { base.SetValue(EnabledProperty, value); }
        }

        public static DependencyProperty FontColorProperty =
        DependencyProperty.Register(
          "FontColor",
          typeof(Brush), typeof(AxisLabels),
          new PropertyMetadata(OnFontColorPropertyChanged));

        private static void OnFontColorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            AxisLabels _AxisLabels = d as AxisLabels;
            _AxisLabels.UpdateVisual("FontColor",e.NewValue);
            _AxisLabels.FirePropertyChanged("FontColor");
        }

        public Brush FontColor
        {
            get { return ((Brush)base.GetValue(FontColorProperty)); }
            set { base.SetValue(FontColorProperty, value); }
        }

        public static DependencyProperty TextWrapProperty =
        DependencyProperty.Register(
          "TextWrap",
          typeof(TextWrapping), typeof(AxisLabels),
          new PropertyMetadata(OnTextWrapPropertyChanged));

        private static void OnTextWrapPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            AxisLabels _AxisLabels = d as AxisLabels;
            _AxisLabels.FirePropertyChanged("TextWrap");
        }

        public TextWrapping TextWrap
        {
            get { return ((TextWrapping)base.GetValue(TextWrapProperty)); }
            set { base.SetValue(TextWrapProperty, value); }
        }

        public static DependencyProperty RowsProperty =
        DependencyProperty.Register(
          "Rows",
          typeof(Nullable<int>), typeof(AxisLabels),
          new PropertyMetadata(OnRowsPropertyChanged));

        private static void OnRowsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            AxisLabels _AxisLabels = d as AxisLabels;
            _AxisLabels.FirePropertyChanged("Rows");
        }

        [TypeConverter(typeof(Converters.NullableIntConverter))]
        public Nullable<int> Rows
        {
            get
            {
                if ((Nullable<int>)GetValue(RowsProperty) == null)
                    return 0;
                else
                    return ((Nullable<int>) base.GetValue(RowsProperty));
            }
            set { base.SetValue(RowsProperty, value); }
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public override string ToolTipText
        {
            get
            {
                throw new NotImplementedException("Tooltiptext property for axislabels is not implemented");
            }
            set
            {
                throw new NotImplementedException("Tooltiptext property for axislabels is not implemented");
            }
        }

        public new double Opacity
        {
            get
            {
                return (double) GetValue(OpacityProperty);
            }
            set
            {
                if(Opacity !=value)
                {
                    SetValue(OpacityProperty,value);
                    FirePropertyChanged("Opacity");
                }
            }
        }

        public new FontFamily FontFamily
        {
            get
            {
                if ((FontFamily)GetValue(FontFamilyProperty) == null)
                    return new FontFamily("Arial");
                else
                    return (FontFamily) GetValue(FontFamilyProperty);
            }
            set
            {
                if(FontFamily !=value)
                {
                    SetValue(FontFamilyProperty,value);
                    FirePropertyChanged("FontFamily");
                }
            }
        }

        public new FontStyle FontStyle
        {
            get
            {
                return (FontStyle) GetValue(FontStyleProperty);
            }
            set
            {
                if(FontStyle!=value)
                {
                    SetValue(FontStyleProperty,value);
                    UpdateVisual("FontStyle",value);
                }
            }
        }

        public new FontWeight FontWeight
        {
            get
            {
                return (FontWeight) GetValue(FontWeightProperty);
            }
            set
            {
                if(FontWeight!=value)
                {
                    SetValue(FontWeightProperty,value);
                    UpdateVisual("FontWeight",value);
                }
            }
        }

        public new double FontSize
        {
            get
            {
                return (double) GetValue(FontSizeProperty);
            }
            set
            {
                if(FontSize!=value)
                {
                    SetValue(FontSizeProperty,value);
                    FirePropertyChanged("FontSize");
                }
            }
        }

        public new Axis Parent
        {
            get
            {
                return _Parent;
            }
            set
            {
                Debug.Assert(typeof(Axis).Equals(value.GetType()),"Unknown parent","DataPoint should have DataSeries as parent");
                _Parent = value;
            }
        }

        internal Canvas Visual { get; private set; }
        internal double Minimum { get; set; }
        internal double Maximum{ get; set;}
        internal double DataMinimum { get; set; }
        internal double DataMaximum { get; set; }
        internal new double Width { get; set; }
        internal new double Height { get; set; }
        internal PlacementTypes Placement { get; set; }
        internal Dictionary<double, string> AxisLabelContentDictionary { get; set; }
        internal bool AllAxisLabels { get; set; }
        internal Axis ParentAxis { get; set; }
        internal double TopOverflow { get; set; }
        internal double BottomOverflow { get; set; }
        internal double LeftOverflow { get; set; }
        internal double RightOverflow { get; set; }
        internal List<AxisLabel> AxisLabelList { get; set; }
        internal List<double> LabelValues { get; set; }

        private new static readonly DependencyProperty ToolTipTextProperty = DependencyProperty.Register
            ("ToolTipText",
            typeof(String),
            typeof(AxisLabels),
            null);
        #endregion

        #region Internal Methods

        /// <summary>
        /// Update visual used for partial update
        /// </summary>
        /// <param name="propertyName">Name of the property</param>
        /// <param name="value">Value of the property</param>
        internal override void UpdateVisual(string propertyName, object value)
        {
            if (Visual != null)
            {
                foreach (AxisLabel axisLabel in AxisLabelList)
                {
                    ApplyAxisLabelFontProperties(axisLabel);
                    axisLabel.ApplyProperties(axisLabel);
                }
            }
            else
                FirePropertyChanged(propertyName);
        }

        /// <summary>
        /// Creates a visual object for the element
        /// </summary>
        internal void CreateVisualObject()
        {
            // Create a new 
            Visual = new Canvas();

            if (!(Boolean)Enabled)
            {
                Visual = null;
                return;
            }

            // Create new Labels list
            AxisLabelList = new List<AxisLabel>();

            // List to store the values for which the labels are created
            LabelValues = new List<Double>();

            if (FontSize > _SavedFontSize || Angle != _SavedAngle || Rows != _SavedRows)
                _IsRedraw = false;

            // check if this is a first time draw or a redraw
            if (_IsRedraw)
            {
                // if redraw then restore the original values
                Angle = _SavedAngle;
                FontSize = _SavedFontSize;
                Rows = _SavedRows;
            }
            else
            {
                // Preserve the original values for future use
                _SavedAngle = (Double)Angle;
                _SavedFontSize = FontSize;
                _SavedRows = (Int32)Rows;
                _IsRedraw = true;
            }

            // create the required labels
            CreateLabels();

            // Set the position of the labels
            SetLabelPosition();

            Visual.Opacity = Opacity;
        }

        #endregion

        #region private methods
        private AxisLabel CreateLabel(string text)
        {
            AxisLabel label=new AxisLabel();
            label.Text = text;
            label.Placement = this.Placement;
            return label;
        }

        private double CalculateAutoInterval(double currentInterval, double axisWidth, int noOfLabels, double angle, int rows)
        {
            double retVal = 1;
            angle = double.IsNaN(angle) ? 0 : angle;
            CalculateHorizontalOverflow();
            return retVal;
        }

        private Boolean CreateLabels()
        {
            // Calculate interval
            Double interval = (Double)Interval;

            if (Double.IsNaN(interval) || interval <= 0)
                interval = ParentAxis.InternalInterval;

            // Set the begining of the axis labels same as that of the Axis Minimum.
            Decimal index = (Decimal)Minimum;

            // initialize the loop begin and end parameters
            Decimal minval = (Decimal)Minimum;
            Decimal maxVal = (Decimal)Maximum;

            // initialize the loop index increment values
            Decimal gap = (Decimal)interval;
            Int32 count = 0;

            // if the axis labels belong to axis x
            if (ParentAxis.AxisRepresentation == AxisRepresentations.AxisX)
            {
                // if the data minimum - interval is less than the actual minimum
                if ((DataMinimum - interval) < Minimum)
                    index = (Decimal)DataMinimum;

                if (AllAxisLabels && AxisLabelContentDictionary.Count > 0)
                {
                    Dictionary<Double, String>.Enumerator enumerator = AxisLabelContentDictionary.GetEnumerator();
                    enumerator.MoveNext();

                    Int32 dictionaryIndex = 0;
                    index = (Decimal)enumerator.Current.Key;

                    for (; dictionaryIndex < AxisLabelContentDictionary.Count - 1; dictionaryIndex++)
                    {
                        enumerator.MoveNext();
                        index = Math.Min(index, (Decimal)enumerator.Current.Key);
                    }

                    enumerator.Dispose();
                }

                minval = index;

                if (minval != maxVal)
                {
                    for (; index <= maxVal; index = minval + (++count) * gap)
                    {
                        if ((AllAxisLabels) && (AxisLabelContentDictionary.Count > 0) && (index > (Decimal)DataMaximum))
                            continue;

                        String labelContent = "";

                        if (AxisLabelContentDictionary.ContainsKey((Double)index))
                        {
                            if (ParentAxis.AxisOrientation == Orientation.Vertical)
                                labelContent = GetFormattedMultilineText(AutoFormatMultilineText(AxisLabelContentDictionary[(Double)index]));
                            else
                                labelContent = GetFormattedMultilineText(AxisLabelContentDictionary[(Double)index]);
                        }
                        else
                            labelContent = GetFormattedString((Double)index);

                        AxisLabel label = CreateLabel(labelContent);

                        AxisLabelList.Add(label);
                        LabelValues.Add((Double)index);
                    }
                }
                else
                {
                    return false;
                }
            }
            else
            {
                if (minval != maxVal)
                {
                    AxisLabel label;

                    // Create and save the first label
                    label = CreateLabel(GetFormattedString(Minimum));
                    AxisLabelList.Add(label);
                    LabelValues.Add(Minimum);

                    // Shift the maximum inwards so that the last label is not created
                    // the reason for this is some times due to double approximation the last label doesnt get created
                    // thats why the last label is explicitly created and hence must not be created in the following loop
                    maxVal = (Decimal)(Maximum - interval / 2);

                    //Create and save intermediate labels
                    for (index = minval + (++count) * gap; index <= maxVal; index = minval + (++count) * gap)
                    {
                        label = CreateLabel(GetFormattedString((Double)index));
                        AxisLabelList.Add(label);
                        LabelValues.Add((Double)index);
                    }

                    //create and save the last label
                    label = CreateLabel(GetFormattedString(Maximum));
                    AxisLabelList.Add(label);
                    LabelValues.Add(Maximum);
                }
                else
                {
                    return false;
                }
            }

            return true;
        }

        private String AutoFormatMultilineText(String text)
        {
            String multiLineText = "";
            Int32 charCount = 0;
            foreach (Char c in text)
            {
                if (c != ' ')
                {
                    charCount++;
                    multiLineText += c;
                }
                else if (charCount >= 15)
                {
                    multiLineText += "\n";
                    charCount = 0;
                }
                else
                    multiLineText += c;
            }

            multiLineText = GetFormattedMultilineText(multiLineText);
            return multiLineText;
        }

        private void SetLabelPosition()
        {
            switch (Placement)
            {
                case PlacementTypes.Top:
                    PositionLabelsTop();
                    break;
                case PlacementTypes.Left:
                    PositionLabelsLeft();
                    break;
                case PlacementTypes.Right:
                    PositionLabelsRight();
                    break;
                case PlacementTypes.Bottom:
                    PositionLabelsBottom();
                    break;
            }
        }

        /// <summary>
        /// Labels will be positioned for the axis that will appear above the plot area
        /// </summary>
        private void PositionLabelsTop()
        {
            Double startOffset = Double.IsNaN(ParentAxis.StartOffset) ? 0 : ParentAxis.StartOffset;
            Double endOffset = Double.IsNaN(ParentAxis.EndOffset) ? 0 : ParentAxis.EndOffset;

            // Check if the width is valid or not
            if (Double.IsNaN(Width) || Width <= 0)
                return;

            // set the width of the canvas
            Visual.Width = Width;

            // Variable to calculate the height of the visual canvas
            Double height = 0;

            // Calculate Default placement values
            CalculateHorizontalDefaults();

            // Calculate the height of the labels
            for (Int32 i = 0; i < AxisLabelList.Count; i++)
            {
                AxisLabel label = AxisLabelList[i];

                //Set size affecting font properties
                ApplyAxisLabelFontProperties(label);

                // set the label position
                label.Position = new Point(0, 0);

                // create the label visual element
                label.CreateVisualObject();

                // get the max height of the labels
                height = Math.Max(Math.Max(height, label.ActualHeight), _MaxRowHeight);

            }

            for (Int32 i = 0; i < AxisLabelList.Count; i++)
            {
                AxisLabel label = AxisLabelList[i];

                // get the position of the label
                Double position = Graphics.ValueToPixelPosition(startOffset, Width - endOffset, Minimum, Maximum, LabelValues[i]);

                //Calculate vertical Position
                Double top = 0;
                if (GetAngle() != 0)
                {
                    top = Math.Abs((label.ActualTextHeight / 2) * Math.Sin(Math.PI / 2 - AxisLabel.GetRadians(GetAngle())));
                }

                // Set the new position
                label.Position = new Point(position, height * (Int32)Rows - top - ((i % (Int32)Rows) * _MaxRowHeight) + Padding.Top);

                // Create the visual element again
                label.CreateVisualObject();

                // add the element to the visual canvas
                Visual.Children.Add(label.Visual);

            }

            // set the height of the visual canvas
            Visual.Height = height * (Int32)Rows + Padding.Top;

            // calculate the overflow due to this set of axis labels
            CalculateHorizontalOverflow();
        }

        /// <summary>
        /// Labels will be positioned for the axis that will appear to the left of the plot area
        /// </summary>
        private void PositionLabelsLeft()
        {
            Double startOffset = Double.IsNaN(ParentAxis.StartOffset) ? 0 : ParentAxis.StartOffset;
            Double endOffset = Double.IsNaN(ParentAxis.EndOffset) ? 0 : ParentAxis.EndOffset;

            // Check if the height is valid or not
            if (Double.IsNaN(Height) || Height <= 0)
                return;

            // set the height of the canvas
            Visual.Height = Height;

            // Variable to calculate the height of the visual canvas
            Double width = 0;

            //Calculate Defaults for the vertical axis
            CalculateVerticalDefaults();

            // Calculate the width of the labels
            for (Int32 i = 0; i < AxisLabelList.Count; i++)
            {
                AxisLabel label = AxisLabelList[i];

                // set size affecting pararameters
                ApplyAxisLabelFontProperties(label);

                // set the label position
                label.Position = new Point(0, 0);

                // create the label visual element
                label.CreateVisualObject();

                // get the max width of the labels
                width = Math.Max(width, label.ActualWidth);
            }

            for (Int32 i = 0; i < AxisLabelList.Count; i++)
            {
                AxisLabel label = AxisLabelList[i];

                // get the position of the label
                Double position = Graphics.ValueToPixelPosition(Height - endOffset, startOffset, Minimum, Maximum, LabelValues[i]);

                //Calculate horizontal Position
                Double left = 0;
                if (GetAngle() != 0)
                {
                    left = Math.Abs((label.ActualTextHeight / 2) * Math.Cos(Math.PI / 2 - AxisLabel.GetRadians(GetAngle())));
                }

                // Set the new position
                label.Position = new Point(width - left + Padding.Left, position);

                // Create the visual element again
                label.CreateVisualObject();

                // add the element to the visual canvas
                Visual.Children.Add(label.Visual);

            }

            // set the width of the visual canvas
            Visual.Width = width + Padding.Left;

            // calculate the overflow due to this set of axis labels
            CalculateVerticalOverflow();
        }

        /// <summary>
        /// Labels will be positioned for the axis that will appear to the right of the plot area
        /// </summary>
        private void PositionLabelsRight()
        {
            Double startOffset = Double.IsNaN(ParentAxis.StartOffset) ? 0 : ParentAxis.StartOffset;
            Double endOffset = Double.IsNaN(ParentAxis.EndOffset) ? 0 : ParentAxis.EndOffset;

            // Check if the height is valid or not
            if (Double.IsNaN(Height) || Height <= 0)
                return;

            // set the height of the canvas
            Visual.Height = Height;

            // Variable to calculate the height of the visual canvas
            Double width = 0;

            //Calculate Defaults for the vertical axis
            CalculateVerticalDefaults();

            // Calculate the width of the labels
            for (Int32 i = 0; i < AxisLabelList.Count; i++)
            {
                AxisLabel label = AxisLabelList[i];

                //Set size affecting font properties
                ApplyAxisLabelFontProperties(label);

                // get the position of the label
                Double position = Graphics.ValueToPixelPosition(Height - endOffset, startOffset, Minimum, Maximum, LabelValues[i]);

                //Calculate horizontal Position
                Double left = 0;
                if (GetAngle() != 0)
                {
                    left = Math.Abs((label.ActualTextHeight / 2) * Math.Cos(Math.PI / 2 - AxisLabel.GetRadians(GetAngle())));
                }

                // Set the new position
                label.Position = new Point(0, position);

                // Create the visual element again
                label.CreateVisualObject();

                // add the element to the visual canvas
                Visual.Children.Add(label.Visual);

                // get the max width of the labels
                width = Math.Max(width, label.ActualWidth);
            }

            // set the width of the visual canvas
            Visual.Width = width + Padding.Right;

            // calculate the overflow due to this set of axis labels
            CalculateVerticalOverflow();
        }

        /// <summary>
        /// Labels will be positioned for the axis that will appear to the bottom of the plot area
        /// </summary>
        private void PositionLabelsBottom()
        {
            Double startOffset = Double.IsNaN(ParentAxis.StartOffset) ? 0 : ParentAxis.StartOffset;
            Double endOffset = Double.IsNaN(ParentAxis.EndOffset) ? 0 : ParentAxis.EndOffset;

            // Check if the width is valid or not
            if (Double.IsNaN(Width) || Width <= 0)
                return;

            // set the width of the canvas
            Visual.Width = Width;

            // Variable to calculate the height of the visual canvas
            Double height = 0;

            // Calculate Default placement values
            CalculateHorizontalDefaults();

            // Calculate the height of the labels and position them
            for (Int32 i = 0; i < AxisLabelList.Count; i += (ParentAxis.SkipOfset + 1))
            {
                AxisLabel label = AxisLabelList[i];

                //Set size affecting font properties
                ApplyAxisLabelFontProperties(label);

                // get the position of the label
                Double position = Graphics.ValueToPixelPosition(startOffset, Width - endOffset, Minimum, Maximum, LabelValues[i]);

                //Calculate vertical Position
                Double top = 0;
                if (GetAngle() != 0)
                {
                    top = Math.Abs((label.ActualTextHeight / 2) * Math.Sin(Math.PI / 2 - AxisLabel.GetRadians(GetAngle())));
                }

                // Set the new position
                label.Position = new Point(position, top + ((i % (Int32)Rows) * _MaxRowHeight));

                // Create the visual element again
                label.CreateVisualObject();

                // add the element to the visual canvas
                Visual.Children.Add(label.Visual);

                // get the max height of the labels
                height = Math.Max(Math.Max(height, label.ActualHeight), _MaxRowHeight);
            }

            // set the height of the visual canvas
            Visual.Height = height * (Int32)Rows + Padding.Bottom;

            // calculate the overflow due to this set of axis labels
            CalculateHorizontalOverflow();
        }

        private void ApplyAxisLabelFontProperties(AxisLabel label)
        {
            //Set size affecting font properties
            label.FontSize = FontSize;
            label.FontColor = Charts.Chart.CalculateFontColor((Chart as Chart), FontColor, false);
            label.FontFamily = FontFamily;
            label.FontStyle = FontStyle;
            label.FontWeight = FontWeight;
            label.Angle = GetAngle();
        }

        /// <summary>
        /// This is for axis with placement setting as top or bottom
        /// </summary>
        private void CalculateHorizontalOverflow()
        {
            // Check if the label list contains any labels or not (if not then set the overflow to 0)
            if (AxisLabelList.Count > 0)
            {
                LeftOverflow = (from axisLabel in AxisLabelList select axisLabel.ActualLeft).Min();
                RightOverflow = (from axisLabel in AxisLabelList select (axisLabel.ActualLeft + axisLabel.ActualWidth)).Max() - Width;
            }
            else
            {
                LeftOverflow = 0;
                RightOverflow = 0;
            }

            // if over flow is negative only then an actual overflow has ocured
            if ((Boolean)ParentAxis.Enabled)
                LeftOverflow = LeftOverflow > 0 ? 0 : Math.Abs(LeftOverflow);
            else
                LeftOverflow = 0;

            // if over flow is positive only then an actual overflow has ocured
            RightOverflow = RightOverflow < 0 ? 0 : RightOverflow;

            // For top or bottom these will remain zero
            TopOverflow = 0;
            BottomOverflow = 0;
        }

        /// <summary>
        /// This is for axis with placement setting as left or right
        /// </summary>
        private void CalculateVerticalOverflow()
        {
            // Check if the label list contains any labels or not (if not then set the overflow to 0)
            if (AxisLabelList.Count > 0)
            {
                TopOverflow = (from axisLabel in AxisLabelList select axisLabel.ActualTop).Min();
                BottomOverflow = ((from axisLabel in AxisLabelList select (axisLabel.ActualTop + axisLabel.ActualHeight)).Max()) - Height;
            }
            else
            {
                TopOverflow = 0;
                BottomOverflow = 0;
            }

            // if over flow is negative only then an actual overflow has ocured
            TopOverflow = TopOverflow > 0 ? 0 : Math.Abs(TopOverflow);

            // if over flow is positive only then an actual overflow has ocured
            BottomOverflow = BottomOverflow < 0 ? 0 : BottomOverflow;

            // For left or right these will remain zero
            LeftOverflow = 0;
            RightOverflow = 0;
        }

        /// <summary>
        /// Returns the proper angle value for calculation
        /// </summary>
        private Double GetAngle()
        {
            return Double.IsNaN((Double)this.Angle) ? 0 : (Double)this.Angle;
        }

        /// <summary>
        /// Calculates default font size based on a scaling criteria
        /// </summary>
        /// <param name="area">Double</param>
        /// <returns>FontSize as Double</returns>
        private Double CalculateFontSize(Double area)
        {
            if (Double.IsNaN(FontSize) || FontSize <= 0)
            {
                return Graphics.DefaultFontSizes[1];
            }
            else
                return FontSize;
        }

        /// <summary>
        /// Calculate auto font size
        /// </summary>
        /// <param name="initialFontSize">Double</param>
        /// <param name="width">Double</param>
        /// <returns>FontSize as Double</returns>
        private Double AutoAdjustFontSize(Double initialFontSize, Double width)
        {
            Double minimumFontSize = 8;
            Double fontSize = initialFontSize;
            TextBlock textBlock = new TextBlock();

            Double labelsWidth = 0;
            Size textBlockSize;
            for (; fontSize > minimumFontSize; fontSize -= 2)
            {
                textBlock.FontSize = fontSize;
                labelsWidth = 0;

                foreach (AxisLabel label in AxisLabelList)
                {
                    textBlock.Text = " " + label.Text + " ";
                    textBlockSize = Graphics.CalculateTextBlockSize(AxisLabel.GetRadians(GetAngle()), textBlock);
                    labelsWidth += textBlockSize.Width;
                }

                if (labelsWidth <= width)
                    break;
            }

            return fontSize;
        }

        /// <summary>
        /// Calculate number of rows for axislabels
        /// </summary>
        /// <returns></returns>
        private Int32 CalculateNumberOfRows()
        {
            if (Rows <= 0)
            {
                TextBlock textBlock = new TextBlock();
                textBlock = SetFontProperties(textBlock);

                //Calculate interval
                Double interval = (Double)((Double.IsNaN((Double)Interval) && Interval <= 0) ? Interval : ParentAxis.InternalInterval);

                Double pixelInterval = Graphics.ValueToPixelPosition(0, Width, Minimum, Maximum, interval + Minimum);
                List<Double> labelWidths = new List<Double>();
                Double maxRowHeight = 0;
                Size textBlockSize;

                foreach (AxisLabel label in AxisLabelList)
                {
                    textBlock.Text = " " + label.Text + " ";
                    textBlockSize = Graphics.CalculateTextBlockSize(AxisLabel.GetRadians(GetAngle()), textBlock);
                    maxRowHeight = Math.Max(maxRowHeight, textBlockSize.Height);
                    labelWidths.Add(textBlockSize.Width);
                }

                _MaxRowHeight = maxRowHeight;

                Boolean overlap;
                Int32 rows;
                for (rows = 1; rows <= 3; rows++)
                {
                    overlap = false;
                    for (Int32 i = 0; i < labelWidths.Count - rows; i++)
                    {
                        Double labelFittingSize = labelWidths[i] / 2 + labelWidths[i + rows] / 2;
                        if (labelFittingSize > pixelInterval * rows)
                        {
                            overlap = true;
                            break;
                        }
                    }
                    if (!overlap)
                        break;
                }

                return rows;
            }
            else
                return (Int32)Rows;
        }

        /// <summary>
        /// Set properties of a textblock
        /// </summary>
        /// <param name="textBlock">TextBlock</param>
        /// <returns>TextBlock</returns>
        private TextBlock SetFontProperties(TextBlock textBlock)
        {
            textBlock.FontSize = FontSize;
            /* set other font properties */
            textBlock.FontFamily = FontFamily;
            textBlock.FontStyle = FontStyle;
            textBlock.FontWeight = FontWeight;

            return textBlock;
        }

        /// <summary>
        /// Get max height of axislabels
        /// </summary>
        /// <returns>Double</returns>
        private Double GetMaxHeight()
        {
            TextBlock textBlock = new TextBlock();

            textBlock = SetFontProperties(textBlock);

            Double maxRowHeight = 0;
            Size textBlockSize;

            Int32 labelIndex = 0;
            for (labelIndex = 0; labelIndex < AxisLabelList.Count; labelIndex += (ParentAxis.SkipOfset + 1))
            {
                AxisLabel label = AxisLabelList[labelIndex];
                textBlock.Text = label.Text;
                textBlockSize = Graphics.CalculateTextBlockSize(AxisLabel.GetRadians(GetAngle()), textBlock);
                maxRowHeight = Math.Max(maxRowHeight, textBlockSize.Height);
            }

            return maxRowHeight;
        }

        /// <summary>
        /// Calculate default values for angle and rows of axis
        /// </summary>
        private void CalculateHorizontalDefaults()
        {
            IsNotificationEnable = false;

            Double width = Double.IsNaN(Width) ? 0 : Width;
            Double height = Double.IsNaN(Height) ? 0 : Height;
            Double max = Math.Max(width, height);
            if (Double.IsNaN(FontSize) || FontSize <= 0)
            {
                Double initialFontSize = CalculateFontSize(max);
                FontSize = initialFontSize;
            }
            if (Rows <= 0)
            {
                Int32 rows = CalculateNumberOfRows();

                if (rows > 2 && Double.IsNaN((Double)Angle))
                {
                    Rows = 1;
                    Angle = -45;

                    if (Double.IsNaN((Double)ParentAxis.Interval) && Double.IsNaN((Double)Interval))
                        ParentAxis.SkipOfset = CalculateSkipOffset((int)Rows, (Double)Angle, Width);
                    else
                        ParentAxis.SkipOfset = 0;
                }
                else
                {
                    Rows = rows;
                }
            }
            else
            {
                Int32 rows = CalculateNumberOfRows();

                if (rows > 2 && Double.IsNaN((Double)Angle))
                {
                    Angle = -45;
                }
            }

            _MaxRowHeight = GetMaxHeight();

            IsNotificationEnable = true;
        }

        /// <summary>
        /// Calculate skip offset for axis labels
        /// </summary>
        /// <param name="noOfRows">Number of rows</param>
        /// <param name="angle">Rotation angle of labels</param>
        /// <param name="axisWidth">Width of the axis</param>
        /// <returns>Offset as Int32</returns>
        private Int32 CalculateSkipOffset(Int32 noOfRows, Double angle, Double axisWidth)
        {
            Int32 skipOffset = 0;             // Skip offset
            Boolean overlap = true;

            Double interval = (Double)((Double.IsNaN((Double)Interval) && Interval <= 0) ? Interval : ParentAxis.InternalInterval);
            //Double interval = (Double)((Double.IsNaN((Double)Interval) || Interval <= 0) ? ParentAxis.InternalInterval : Interval);
            Double pixelInterval;
            TextBlock textBlock = new TextBlock();
            textBlock = SetFontProperties(textBlock);
            textBlock.Text = "ABCD";

#if WPF
            textBlock.Measure(new Size(Double.MaxValue, Double.MaxValue));
#endif

            while (overlap)
            {
                pixelInterval = Graphics.ValueToPixelPosition(0, Width, Minimum, Maximum, interval + skipOffset + Minimum);

                if (pixelInterval >= textBlock.ActualHeight)
                {
                    overlap = false;
                }
                else
                    skipOffset++;
            }

            return skipOffset;
        }

        /// <summary>
        /// Returns formatted string from a given value depending upon scaling set and value format string
        /// </summary>
        /// <param name="value">Double value</param>
        /// <returns>String</returns>
        private String GetFormattedString(Double value)
        {
            return (ParentAxis != null) ? ParentAxis.GetFormattedString(value) : value.ToString();
        }

        /// <summary>
        ///  Calculate default values for vertical axis
        /// </summary>
        private void CalculateVerticalDefaults()
        {
            Double width = Double.IsNaN(Width) ? 0 : Width;
            Double height = Double.IsNaN(Height) ? 0 : Height;
            Double max = Math.Max(width, height);
            if (Double.IsNaN(FontSize) || FontSize <= 0)
            {
                FontSize = CalculateFontSize(max);
            }
        }
        #endregion

    }
}
